package com.birdcage.gamemanagers;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import java.util.HashMap;

enum EStateType
{
    eST_Enter,
    eST_Update,
    eST_Exit
}
class FSMState
{
    ///Construction
    FSMState(String name, Method enter, Method update, Method exit)
    {
        m_name = name;
        m_enter = enter;
        m_update = update;
        m_exit = exit;
    }
    
    public void Run(GameObject object, EStateType type)
    {        
        Method method = null;
        switch (type)
        {
        case eST_Enter:
            method = m_enter;
            break;
        case eST_Update: 
            method = m_update;
            break;
        case eST_Exit:
            method = m_exit;
            break;
        }
        
        if (method != null)
        {
            try
            {
                method.invoke(object);
            } catch (IllegalArgumentException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InvocationTargetException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }    
    
    public void SetName(String m_name)
    {
        this.m_name = m_name;
    }

    public String GetName()
    {
        return m_name;
    }

    
    private String m_name;
    private Method m_enter;
    private Method m_update;
    private Method m_exit;    
}
public class FSM
{
    /// Construction
    public FSM(GameObject parent)
    {
        m_parent = parent;
    }
    public void AddState(String name, Object object)
    {        
        Method enter = null;
        Method update = null;
        Method exit = null;
        try
        {
            enter = object.getClass().getMethod(name+"_Enter");
            update = object.getClass().getMethod(name+"_Update");
            exit = object.getClass().getMethod(name+"_Exit");
        } catch (SecurityException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        boolean exisiting = m_states.get(name) != null;
        // don't add two state with the same name.
        assert !exisiting && (enter != null) && (update != null) && (exit != null) ;
        
        // set accessibility true so we can call private function.
        enter.setAccessible(true);
        update.setAccessible(true);
        exit.setAccessible(true);
        
        m_states.put(name, new FSMState(name, enter, update, exit));        
    }
    
    public void AddState(String name, Method enter, Method update, Method exit)
    {        
        boolean exisiting = m_states.get(name) != null;
        // don't add two state with the same name.
        assert !exisiting;
        
        // set accessibility true so we can call private function.
        enter.setAccessible(true);
        update.setAccessible(true);
        exit.setAccessible(true);
        
        m_states.put(name, new FSMState(name, enter, update, exit));        
    }
    
    public void InitState(String name)
    {
        m_currentState = m_states.get(name);
        assert m_currentState != null;
    }
    
    public void Update() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
    {
        if (m_currentState != null)
        {
            if (m_stateChanged == true)
            {
                m_currentState.Run(m_parent, EStateType.eST_Enter);
                m_stateChanged = false;
            } 
            else
            {
                m_currentState.Run(m_parent, EStateType.eST_Update);
            }
        }
    }
    
    public boolean SwitchState(boolean condition, String stateName)
    {
        boolean result = false;
        if (condition && stateName != m_currentState.GetName())
        {
            m_previousState = m_currentState;
            m_previousState.Run(m_parent, EStateType.eST_Exit);            
            m_currentState = m_states.get(stateName);
            assert m_currentState != null;
            m_stateChanged = true;
            result = true;
        }
        return result;
    }
    private GameObject m_parent;
    private HashMap<String, FSMState> m_states = new HashMap<String, FSMState>();
    private FSMState m_currentState = null;
    private FSMState m_previousState = null;
    private boolean m_stateChanged = true;
    
}
